/*
* Copyright 1993-2009 NVIDIA Corporation.  All rights reserved.
*
* NOTICE TO USER:
*
* This source code is subject to NVIDIA ownership rights under U.S. and
* international Copyright laws.  Users and possessors of this source code
* are hereby granted a nonexclusive, royalty-free license to use this code
* in individual and commercial software.
*
* NVIDIA MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF THIS SOURCE
* CODE FOR ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR
* IMPLIED WARRANTY OF ANY KIND.  NVIDIA DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOURCE CODE, INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE.
* IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL,
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS,  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION,  ARISING OUT OF OR IN CONNECTION WITH THE USE
* OR PERFORMANCE OF THIS SOURCE CODE.
*
* U.S. Government End Users.   This source code is a "commercial item" as
* that term is defined at  48 C.F.R. 2.101 (OCT 1995), consisting  of
* "commercial computer  software"  and "commercial computer software
* documentation" as such terms are  used in 48 C.F.R. 12.212 (SEPT 1995)
* and is provided to the U.S. Government only as a commercial end item.
* Consistent with 48 C.F.R.12.212 and 48 C.F.R. 227.7202-1 through
* 227.7202-4 (JUNE 1995), all U.S. Government End Users acquire the
* source code with only those rights set forth herein.
*
* Any use of this source code in individual and commercial software must
* include, in the user documentation and internal comments to the code,
* the above Disclaimer and U.S. Government End Users Notice.
*/

/* This example demonstrates how to use the CUDA Direct3D bindings to fill 
* a vertex buffer with CUDA and use Direct3D to render the data.
* Host code.
*/

#pragma warning(disable: 4312)

#include <windows.h>
#include <mmsystem.h>

// this header inclues all the necessary D3D10 includes
#include <DXUT/DXUT.h>
#include <DXUT/DXUTmisc.h>
//#include <dynlink_d3d10.h>

// includes, cuda
#include <cuda.h>
#include <builtin_types.h>
#include <cuda_runtime_api.h>
#include <cuda_d3d10_interop.h>

// includes, project
#include <cutil_inline.h>
//#include <rendercheck_d3d10.h>



#include "Types.h"
#include <Settings.h>
#include <Game.h>


// Nuestra clase principal del GPU
Game game;




// Damos por hecho que el que corre este programa posee una tarjeta
// compatible con CUDA.
bool CALLBACK IsD3D10DeviceAcceptable( UINT Adapter, UINT Output, D3D10_DRIVER_TYPE DeviceType,
                                       DXGI_FORMAT BufferFormat, bool bWindowed, void* pUserContext )
{
    return true;
}


// Inicializamos nuestras estructuras de datos.
HRESULT CALLBACK OnD3D10CreateDevice( ID3D10Device* pd3dDevice, const DXGI_SURFACE_DESC* pBufferSurfaceDesc,
                                      void* pUserContext )
{
    game.device = pd3dDevice;
    game.Initialize();
    return S_OK;
}

// Aqui se (re)crean elementos que dependan del backbuffer y su tamano
HRESULT CALLBACK OnD3D10ResizedSwapChain( ID3D10Device* pd3dDevice, IDXGISwapChain* pSwapChain,
                                          const DXGI_SURFACE_DESC* pBufferSurfaceDesc, void* pUserContext )
{
    game.ResizeSwapChain(pd3dDevice, pSwapChain, pBufferSurfaceDesc, pUserContext);
    return S_OK;
}


// Cuando se llama esta funcion es porque debemos dibujar.
void CALLBACK OnD3D10FrameRender( ID3D10Device* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
    game.Draw(fElapsedTime);
}

// Liberamos los recursos que creamos en OnD3D10ResizedSwapChain
void CALLBACK OnD3D10ReleasingSwapChain( void* pUserContext )
{
}


// Liberamos los recursos que creamos en OnD3D10CreateDevice 
void CALLBACK OnD3D10DestroyDevice( void* pUserContext )
{
    game.Dispose();
}

// Llamada justo antes de crear el device D3D, podemos cambiar cualquier setting del device que necesitemos.
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext )
{
    return true;
}


// Llamada cuando se necesita actualizar la escena.
void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext )
{
    game.Update(fElapsedTime);
}


// Aqui manejamos los mensajes que se le pasan a nuestra aplicacion
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing,
                          void* pUserContext )
{
    return 0;
}


// Manejamos los eventos de teclado.
void CALLBACK OnKeyboard( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext )
{
    if( bKeyDown )
    {
        switch( nChar )
        {
            case VK_F1: // Change as needed                
                break;
        }
    }
}

mouseState prevMouse;

/* Manejamos los eventos de mouse aqui y le damos facilidad
 * a game, llamando a los manejadores de eventos correspondientes
 * a cada evento.
 */
void CALLBACK OnMouse(bool bLeftButtonDown, 
                      bool bRightButtonDown, 
                      bool bMiddleButtonDown, 
                      bool bSideButton1Down, 
                      bool bSideButton2Down, 
                      int nMouseWheelDelta, 
                      int xPos, 
                      int yPos, 
                      void* pUserContext)
                      
{
    mouseState mouse;
    mouse.left = bLeftButtonDown;
    mouse.right = bRightButtonDown;
    mouse.middle = bMiddleButtonDown;
    mouse.x = xPos;
    mouse.y = yPos;

    if (mouse.x != prevMouse.x || mouse.y != prevMouse.y)
        game.OnMouseMove(mouse.x, mouse.y);
    if (mouse.left && !prevMouse.left)
        game.OnMouseLeftDown(mouse.x, mouse.y);
    if (!mouse.left && prevMouse.left)
        game.OnMouseLeftUp(mouse.x, mouse.y);
    if (nMouseWheelDelta != 0)
        game.OnMouseWheel(mouse.x, mouse.y, nMouseWheelDelta);

    prevMouse = mouse;
}


// Program main
int main(int argc, char** argv)
{
// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif
    // Set DXUT callbacks
    //DXUTSetCallbackD3D10DeviceAcceptable( IsD3D10DeviceAcceptable );
    DXUTSetCallbackD3D10DeviceCreated( OnD3D10CreateDevice );
    DXUTSetCallbackD3D10SwapChainResized( OnD3D10ResizedSwapChain );
    DXUTSetCallbackD3D10SwapChainReleasing( OnD3D10ReleasingSwapChain );
    DXUTSetCallbackD3D10DeviceDestroyed( OnD3D10DestroyDevice );
    DXUTSetCallbackD3D10FrameRender( OnD3D10FrameRender );
    DXUTSetCallbackMouse( OnMouse, true);

    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackKeyboard( OnKeyboard );
    DXUTSetCallbackFrameMove( OnFrameMove );
    DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );

    DXUTInit( true, true, NULL ); // Parse the command line, show msgboxes on error, no extra command line params
    DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen
    DXUTCreateWindow( L"Visualizator 2" );
    DXUTCreateDevice( true, 853, 480 );
    DXUTMainLoop(); // Enter into the DXUT render loop

    //game.Run();

	system("PAUSE");
}